/**
 * 封装 anyRTC Live 类
 */
import EventEmitter from "events";
import type TypedEmitter from "typed-emitter";
import type {
  ClientRole,
  IArRTCRemoteUser,
  ConnectionState,
  ConnectionDisconnectedReason,
  VideoEncoderConfigurationPreset,
  VideoPlayerConfig,
} from "ar-rtc-sdk";
import type { RtmStatusCode, RtmMessage, ChannelAttributes } from "ar-rtm-sdk";
import { AnyRTCLiveConfig, AnyRTCLiveEvents } from "./liveType";
import { RTCService } from "./rtcService";
import { RTMService } from "./rtmService";

export default class AnyRTCLive extends (EventEmitter as new () => TypedEmitter<AnyRTCLiveEvents>) {
  // 封装的类实例
  private static instance: AnyRTCLive | null;
  private rtcService: RTCService;
  private rtmService: RTMService;

  private _appId: string = "";
  private _userId: string = "";
  private _roomId: string = "";
  private _rtcToken: string = "";
  private _rtmToken: string = "";

  /**
   * 是否已登录
   *
   * RTM 是否登录成功，所有的信令都基于是 RTM 收发
   *
   * @private
   * @type {boolean}
   */
  private _isLogin: boolean = false;

  /**
   * 是否加入房间
   *
   * @private
   * @type {boolean}
   */
  private _isEnterRoom: boolean = false;

  /**
   * 获取单例实例
   */
  public static getInstance() {
    if (!AnyRTCLive.instance) {
      AnyRTCLive.instance = new AnyRTCLive();
    }
    return AnyRTCLive.instance;
  }

  /**
   * 销毁单例实例
   */
  public static destroyInstance() {
    if (AnyRTCLive.instance !== null) {
      // TODO
      AnyRTCLive.instance = null;
    }
  }

  constructor() {
    super();

    // 创建 RTC 实例
    this.rtcService = new RTCService();
    this.bindRTCEvents();

    // 初始化 RTM
    this.rtmService = new RTMService();
    this.bindRTMEvents();
  }
  /**
   * /////////////////////////////////////////////////////////////////////////////////
   * //
   * //                                   基础接口
   * //
   * /////////////////////////////////////////////////////////////////////////////////
   */
  /** 私有化配置
   *
   * @param {{
   *     rtcConfig?: ConfigParameters, // RTC 网关配置
   *     rtmConfig?: ConfigParameters, // RTM 网关配置
   *   }} options
   */
  setParameters(options: AnyRTCLiveConfig) {
    const { rtcConfig, rtmConfig } = options;
    // 设置 RTC 私有云配置
    if (rtcConfig) {
      this.rtcService.setParameters(rtcConfig);
    }
    // 设置 RTM 私有云配置
    if (rtmConfig) {
      this.rtmService.setParameters(rtmConfig);
    }
  }
  /**
   * token 即将过期，更新token
   */
  updataToken(type: "RTM" | "RTC", token: string) {
    // 更新 RTM 网关 token
    if (type === "RTM") {
      this._rtmToken = token;
      this.rtmService.updataToken(token);
    }
    // 更新 RTC 网关 token
    if (type === "RTC") {
      this._rtcToken = token;
      this.rtcService.updataToken(token);
    }
  }
  /**
   * 主要登录 RTM
   *
   */
  async login(
    sdkAppId: string,
    userId: string,
    rtcToken?: string,
    rtmToken?: string
  ) {
    if (!this._isLogin) {
      this._appId = sdkAppId;
      this._userId = userId;
      this._rtcToken = rtcToken ?? "";
      this._rtmToken = rtmToken ?? "";
      // 登录 RTM
      await this.rtmService.login(this._appId, userId, rtmToken);
      this._isLogin = true;
    }
  }

  /**
   * 登出
   *
   */
  logout() {
    if (this._isLogin) {
      this.rtcService.leaveChannel();
      this.rtmService.logout();
      this._isLogin = false;
    }
  }

  /**
   * 检查是否已经登录
   *
   */
  checkLogin() {
    return this._isLogin;
  }

  /**
   * 加入房间
   *
   * RTM 加入频道
   * RTC 加入房间
   *
   * @param {string} roomId
   */
  async joinRoom(
    roomId: string,
    role: ClientRole
    // roomParam: { openCamera: boolean; openMicrophone: boolean }
  ) {
    this._isEnterRoom = true;
    this._roomId = roomId;
    // RTM join Channel
    await this.rtmService.joinChannel(roomId);
    // RTC 加入房间
    await this.rtcService.joinChannel(
      this._appId,
      roomId,
      this._rtcToken,
      this._userId,
      role
    );
  }

  /**
   * /////////////////////////////////////////////////////////////////////////////////
   * //
   * //                                   RTC 相关
   * //
   * /////////////////////////////////////////////////////////////////////////////////
   */
  private _onRTCConnectionStateChange(
    curState: ConnectionState,
    revState: ConnectionState,
    reason?: ConnectionDisconnectedReason
  ) {
    this.emit("RTCConnectionStateChange", curState, revState, reason);
  }
  private _onUserEnterRoom(user: IArRTCRemoteUser) {
    this.emit("UserEnterRoom", user);
  }

  private _onUserLeaveRoom(user: IArRTCRemoteUser) {
    this.emit("UserLeaveRoom", user);
  }

  private _onUserAVEnabled(
    user: IArRTCRemoteUser,
    mediaType: "audio" | "video"
  ) {
    this.emit("UserAVEnabled", user, mediaType);
  }

  private _onUserAVDisabled(
    user: IArRTCRemoteUser,
    mediaType: "audio" | "video"
  ) {
    this.emit("UserAVDisabled", user, mediaType);
  }

  private _onRTCTokenWillExpired() {
    this.emit("TokenWillExpired", "RTC");
  }

  private _onRTCTokenDidExpired() {
    this.emit("TokenDidExpired", "RTC");
  }

  private bindRTCEvents() {
    this._onRTCConnectionStateChange =
      this._onRTCConnectionStateChange.bind(this);
    this._onUserEnterRoom = this._onUserEnterRoom.bind(this);
    this._onUserLeaveRoom = this._onUserLeaveRoom.bind(this);
    this._onUserAVEnabled = this._onUserAVEnabled.bind(this);
    this._onUserAVDisabled = this._onUserAVDisabled.bind(this);

    this._onRTCTokenWillExpired = this._onRTCTokenWillExpired.bind(this);
    this._onRTCTokenDidExpired = this._onRTCTokenDidExpired.bind(this);

    this.rtcService.on(
      "CconnectionStateChange",
      this._onRTCConnectionStateChange
    );
    // 远端用户加入
    this.rtcService.on("userJoin", this._onUserEnterRoom);
    // 远端用户离开
    this.rtcService.on("userLeft", this._onUserLeaveRoom);
    // 用户发布轨道
    this.rtcService.on("userPublished", this._onUserAVEnabled);
    // 用户取消发布轨道
    this.rtcService.on("userUnpublished", this._onUserAVDisabled);

    this.rtcService.on("RTCTokenWillExpired", this._onRTCTokenWillExpired);
    this.rtcService.on("RTCTokenDidExpired", this._onRTCTokenDidExpired);
  }

  private unbindRTCEvents() {
    this.rtcService.off(
      "CconnectionStateChange",
      this._onRTCConnectionStateChange
    );
    // 远端用户加入
    this.rtcService.off("userJoin", this._onUserEnterRoom);
    // 远端用户离开
    this.rtcService.off("userLeft", this._onUserLeaveRoom);
    // 用户发布轨道
    this.rtcService.off("userPublished", this._onUserAVEnabled);
    // 用户取消发布轨道
    this.rtcService.off("userUnpublished", this._onUserAVDisabled);
  }

  getMediaDeviceList() {
    return this.rtcService.getMediaDeviceList();
  }

  getCameraList() {
    return this.rtcService.getCameraList();
  }

  getCurrentCameraId() {
    return this.rtcService.currentCamera;
  }

  setCurrentCameraId(deviceId: string) {
    this.rtcService.setCurrentCameraId(deviceId);
  }

  getMicrophoneList() {
    return this.rtcService.getMicrophoneList();
  }

  getCurrentMicrophoneId() {
    return this.rtcService.currentMicrophone;
  }

  setCurrentMicrophoneId(deviceId: string) {
    this.rtcService.setCurrentMicrophoneId(deviceId);
  }

  getSpeakerList() {
    return this.rtcService.getSpeakerList();
  }

  getCurrentSpeakerId() {
    return this.rtcService.currentSpeaker;
  }

  setCurrentSpeakerId(deviceId: string) {
    this.rtcService.setCurrentSpeakerId(deviceId);
  }

  setVideoEncoderParam(videoProfile: VideoEncoderConfigurationPreset) {
    this.rtcService.setLocalVideoProfile(videoProfile);
  }
  /**
   * beta 采集并发布摄像头轨道
   *
   * @param {(string | HTMLElement)} element
   */
  async startMediaAndPublish(element: string | HTMLElement) {
    await this.rtcService.startMediaAndPublish(element);
  }

  /**
   * 采集并发布摄像头轨道
   *
   * @param {(string | HTMLElement)} element
   */
  async startCameraPreview(element: string | HTMLElement) {
    await this.rtcService.startCameraPreview(element);
  }

  /**
   * 停止（发布？？）关闭摄像头
   *
   */
  stopCameraPreview() {
    this.rtcService.stopCameraPreview();
  }

  /**
   * 采集并发布麦克风轨道
   *
   */
  async startMicrophone() {
    await this.rtcService.startMicrophone();
  }

  /**
   * 停止（发布？？）关闭麦克风
   *
   */
  stopMicrophone() {
    this.rtcService.stopMicrophone();
  }

  getMicrophoneVolumeLevel() {
    return this.rtcService.getLocalAudioVolumeLevel();
  }

  muteLocalCamera(mute: boolean) {
    this.rtcService.muteLocalCamera(mute);
  }

  muteLocalMicrophone(mute: boolean) {
    this.rtcService.muteLocalMicrophone(mute);
  }

  startRemoteView(
    userId: string,
    element: string | HTMLElement,
    config?: VideoPlayerConfig
  ) {
    this.rtcService.startRemotePreview(userId, element, config);
  }

  // stopRemoteView(userId: string) {
  //   this.rtcService.stopRemotePreview(userId);
  // }

  /**
   * /////////////////////////////////////////////////////////////////////////////////
   * //
   * //                                   RTM 相关
   * //
   * /////////////////////////////////////////////////////////////////////////////////
   */
  /**
   * 发送频道消息
   *
   * @param {string} messageContent
   */
  async sendMessage(messageContent: string) {
    if (this._isEnterRoom) {
      await this.rtmService.sendChannelMessage(this._roomId, messageContent);
    }
  }

  /**
   * 发送点对点消息
   *
   * @param {string} messageContent
   */
  async sendP2PMessage(userId: string, messageContent: string) {
    if (this._isEnterRoom) {
      await this.rtmService.sendP2PMessage(userId, messageContent);
    }
  }

  private _onRTMConnectionStateChange(
    newState: RtmStatusCode.ConnectionState,
    reason: RtmStatusCode.ConnectionChangeReason
  ) {
    this.emit("RTMConnectionStateChange", newState, reason);
  }
  private _onChannelMemberJoined(memberId: string) {
    this.emit("ChannelMemberJoined", memberId);
  }
  private _onChannelMemberLeft(memberId: string) {
    this.emit("ChannelMemberLeft", memberId);
  }
  private _onChannelMessage(message: RtmMessage, memberId: string) {
    this.emit("ChannelMessage", message, memberId);
  }
  private _onPeerMessage(message: RtmMessage, peerId: string) {
    this.emit("UserMessage", message, peerId);
  }
  private _onChannelMemberCountUpdated(memberCount: number) {
    this.emit("ChannelMemberCountUpdated", memberCount);
  }
  private _onChannelAttributesUpdated(attributes: ChannelAttributes) {
    this.emit("ChannelAttributesUpdated", attributes);
  }
  private _onRTMTokenWillExpired() {
    this.emit("TokenWillExpired", "RTM");
  }
  private _onRTMTokenDidExpired() {
    this.emit("TokenDidExpired", "RTM");
  }
  private bindRTMEvents() {
    this._onRTMConnectionStateChange =
      this._onRTMConnectionStateChange.bind(this);
    this._onChannelMemberJoined = this._onChannelMemberJoined.bind(this);
    this._onChannelMemberLeft = this._onChannelMemberLeft.bind(this);
    this._onChannelMessage = this._onChannelMessage.bind(this);
    this._onPeerMessage = this._onPeerMessage.bind(this);
    this._onChannelMemberCountUpdated =
      this._onChannelMemberCountUpdated.bind(this);
    this._onChannelAttributesUpdated =
      this._onChannelAttributesUpdated.bind(this);

    this._onRTMTokenWillExpired = this._onRTMTokenWillExpired.bind(this);
    this._onRTMTokenDidExpired = this._onRTMTokenDidExpired.bind(this);

    this.rtmService.on(
      "ConnectionStateChange",
      this._onRTMConnectionStateChange
    );
    this.rtmService.on("ChannelMemberJoined", this._onChannelMemberJoined);
    this.rtmService.on("ChannelMemberLeft", this._onChannelMemberLeft);
    this.rtmService.on("ChannelMessage", this._onChannelMessage);
    this.rtmService.on("MessageFromPeer", this._onPeerMessage);
    this.rtmService.on(
      "ChannelMemberCountUpdated",
      this._onChannelMemberCountUpdated
    );
    this.rtmService.on(
      "ChannelAttributesUpdated",
      this._onChannelAttributesUpdated
    );

    this.rtmService.on("RTMTokenWillExpired", this._onRTMTokenWillExpired);
    this.rtmService.on("RTMTokenDidExpired", this._onRTMTokenDidExpired);
  }

  private unbindRTMEvents() {
    this.rtmService.off(
      "ConnectionStateChange",
      this._onRTMConnectionStateChange
    );
    this.rtmService.off("ChannelMemberJoined", this._onChannelMemberJoined);
    this.rtmService.off("ChannelMemberLeft", this._onChannelMemberLeft);
    this.rtmService.off("ChannelMessage", this._onChannelMessage);
    this.rtmService.off("MessageFromPeer", this._onPeerMessage);
    this.rtmService.off(
      "ChannelMemberCountUpdated",
      this._onChannelMemberCountUpdated
    );
    this.rtmService.off(
      "ChannelAttributesUpdated",
      this._onChannelAttributesUpdated
    );
    this.rtmService.off("RTMTokenWillExpired", this._onRTMTokenWillExpired);
    this.rtmService.off("RTMTokenDidExpired", this._onRTMTokenDidExpired);
  }
}
